const { ccclass, property } = cc._decorator;
const LayoutType = cc.Enum({
    HORIZOTAL: 1,
    VERTICAL: 2,
    GRID: 3,
})
const LayoutDirection = cc.Enum({
    VERTICAL:2,
    HORIZOTAL:1,
})
const LayoutDirectionHorizotal = cc.Enum({
    LEFT_TO_RIGHT:1,
    RIGHT_TO_LEFT:2,
})
const LayoutDirectionVertical = cc.Enum({
    TOP_TO_BOTTOM:1,
    BOTTOM_TO_TOP:2,
})
class ListPoint{
    lineCount: number;
    colCount: number;
    item: cc.Node;
    index: number;
    info: any;
    pos: cc.Vec2;
}
@ccclass('Padding')
class Padding {
    @property()
    _layoutType: number = LayoutType.VERTICAL;
    @property({
        type: cc.Float,
        visible() {
            return this._layoutType == LayoutType.VERTICAL || LayoutType.GRID == this._layoutType;
        },
    })
    top: number = 0;
    @property({
        type: cc.Float,
        visible() {
            return this._layoutType == LayoutType.VERTICAL || LayoutType.GRID == this._layoutType;
        },
    })
    bottom: number = 0;
    @property({
        type: cc.Float,
        visible() {
            return this._layoutType == LayoutType.HORIZOTAL || LayoutType.GRID == this._layoutType;
        },
    })
    left: number = 0;
    @property({
        type: cc.Float,
        visible() {
            return this._layoutType == LayoutType.HORIZOTAL || LayoutType.GRID == this._layoutType;
        },
    })
    right: number = 0;
    @property({
        type: cc.Float,
        visible() {
            return this._layoutType == LayoutType.HORIZOTAL || LayoutType.GRID == this._layoutType;
        },
    })
    spacingX: number = 0;
    @property({
        type: cc.Float,
        visible() {
            return this._layoutType == LayoutType.VERTICAL || LayoutType.GRID == this._layoutType;
        },
    })
    spacingY: number = 0;
    @property({
        type: LayoutDirectionHorizotal,
        visible() {
            return this._layoutType == LayoutType.HORIZOTAL || LayoutType.GRID == this._layoutType;
        },
    })
    horizotalDirection:number = LayoutDirectionHorizotal.LEFT_TO_RIGHT;
    @property({
        type: LayoutDirectionVertical,
        visible() {
            return this._layoutType == LayoutType.VERTICAL || LayoutType.GRID == this._layoutType;
        },
    })
    verticalDirection:number = LayoutDirectionVertical.TOP_TO_BOTTOM;
    @property({
        type: LayoutDirection,
        visible() {
            return LayoutType.GRID == this._layoutType;
        },
    })
    layoutDirection:number = LayoutDirection.VERTICAL;
}
@ccclass
export default class VirtualList extends cc.Component {
    @property()
    _layoutType: number = LayoutType.VERTICAL;
    @property({
        type: LayoutType,
    })
    set layoutType(num: number) {
        this._layoutType = num;
        if(num == LayoutType.VERTICAL){
            this.padding.layoutDirection = LayoutDirection.VERTICAL;
        }
        if(num == LayoutType.HORIZOTAL){
            this.padding.layoutDirection = LayoutDirection.HORIZOTAL;
        }
        this.padding._layoutType = num;
    }
    get layoutType() {
        return this._layoutType;
    }
    @property({
        type: Padding,
    })
    padding: Padding = new Padding;
    @property({
        type: cc.Node,
    })
    item: cc.Node = null;
    @property({
        type: cc.Component.EventHandler,
    })
    updateItemFun: cc.Component.EventHandler = new cc.Component.EventHandler;
    @property({
        type: cc.Integer,
    })
    bufferSize: number = 1;
    private scrollView: cc.ScrollView;
    private content: cc.Node;
    private view: cc.Node;
    private itemPool: cc.NodePool = new cc.NodePool;
    private data: Array<any> = [];
    private pointMatrix: Array<Array<ListPoint>> = [];
    private lineCount:number = 0;
    private colCount:number = 0;
    private contentArr:ListPoint[] = [];
    onLoad() {
        this.scrollView = this.node.getComponent(cc.ScrollView);
        this.content = this.scrollView.content;
        this.view = this.content.parent;
        this.scrollView.node.on('scrolling', this.updateView, this);
        this.view.on(cc.Node.EventType.SIZE_CHANGED,this.updateView,this);
    }
    start() {
    }
    initVList(data:Array<any>) {
        this.data = data;
        this.getPointMatrix();
    }
    //计算一下行列数
    private countLine_Col() {
        if (this.padding.layoutDirection == LayoutDirection.HORIZOTAL) {//横排布
            let len = this.content.height - this.padding.top - this.padding.bottom + this.padding.spacingY;
            this.lineCount = Math.floor(len / (this.item.height + this.padding.spacingY));
            this.lineCount = this.lineCount < 1 ? 1 : this.lineCount;
            this.lineCount = this.layoutType == LayoutType.HORIZOTAL ? 1 : this.lineCount;
            this.colCount = Math.ceil(this.data.length / this.lineCount);
            this.content.width = this.colCount * (this.padding.spacingX + this.item.width) - this.padding.spacingX + this.padding.left + this.padding.right;
        } else {//纵排布
            let len = this.content.width - this.padding.left - this.padding.right + this.padding.spacingX;
            this.colCount = Math.floor(len / (this.item.width + this.padding.spacingX));
            this.colCount = this.colCount < 1 ? 1 : this.colCount;
            this.colCount = this.layoutType == LayoutType.VERTICAL ? 1 : this.colCount;
            this.lineCount = Math.ceil(this.data.length / this.colCount);
            this.content.height = this.lineCount * (this.padding.spacingY + this.item.height) - this.padding.spacingY + this.padding.top + this.padding.bottom;
        }
    }
    //生成矩阵
    private getPointMatrix(){
        this.countLine_Col();
        let itemWidth = this.item.width;
        let itemHeight = this.item.height;
        let itemAnchorX  = this.item.anchorX;
        let itemAnchorY = this.item.anchorY;
        let contentAnchorX = this.content.anchorX;
        let contentAnchorY = this.content.anchorY;
        let index:number;
        for (let i = 0; i < this.lineCount; i++) {
            this.pointMatrix[i] = [];
            let levelIndex: number;
            let x:number = null;
            let y:number = null;
            y = (1 - contentAnchorY) * this.content.height - this.padding.top - (1 - itemAnchorY) * itemHeight - (itemHeight + this.padding.spacingY) * i;
            y = this.layoutType == LayoutType.HORIZOTAL ? null : y;
            if(this.padding.layoutDirection == LayoutDirection.VERTICAL){//纵
                if (this.padding.verticalDirection == LayoutDirectionVertical.TOP_TO_BOTTOM) {
                    levelIndex = i * this.colCount;
                } else {
                    levelIndex = (this.lineCount - i - 1) * this.colCount;
                }
            }else{
                if (this.padding.verticalDirection == LayoutDirectionVertical.TOP_TO_BOTTOM) {
                    levelIndex = i;
                } else {
                    levelIndex = this.lineCount - i - 1;
                }
            }
            for (let j = 0; j < this.colCount; j++) {
                x = -contentAnchorX * this.content.width + this.padding.left + itemAnchorX * itemWidth + (itemWidth + this.padding.spacingX) * j;
                x = this.layoutType == LayoutType.VERTICAL ? null : x;
                let point = new ListPoint;
                point.lineCount = i + 1;
                point.colCount = j + 1;
                point.pos = cc.v2(x, y);
                if (this.padding.layoutDirection == LayoutDirection.VERTICAL) {
                    if (this.padding.horizotalDirection == LayoutDirectionHorizotal.LEFT_TO_RIGHT) {
                        index = levelIndex + j;
                    } else {
                        index = levelIndex + this.colCount - j - 1;
                    }
                }else{
                    if (this.padding.horizotalDirection == LayoutDirectionHorizotal.LEFT_TO_RIGHT) {
                        index = j * this.lineCount+levelIndex;
                    } else {
                        index = levelIndex + (this.colCount - j - 1) * this.lineCount;
                    }
                }
                point.index = index;
                point.info = this.data[index];
                this.pointMatrix[i][j] = point;
            }
        }
        this.updateView();
    }
    //刷新窗口
    updateView() {
        let viewRect: cc.Rect;
        let x = -this.view.width * this.view.anchorX - this.bufferSize * (this.item.width + this.padding.spacingX);
        let y = -this.view.height * this.view.anchorY - this.bufferSize * (this.item.height + this.padding.spacingY);
        let w = this.view.width + this.bufferSize * 2 * (this.item.width + this.padding.spacingX);
        let h = this.view.height + this.bufferSize * 2 * (this.item.height + this.padding.spacingY);
        viewRect = cc.rect(x, y, w, h);
        //
        this.contentArr.forEach((info)=>{
            this.checkItem(viewRect,info);
        });
        this.contentArr = [];
        //
        let viewPos = this.view.parent.convertToWorldSpaceAR(this.view.position);
        viewPos = this.content.convertToNodeSpaceAR(viewPos);
        let buffWidth = this.bufferSize * 2 * (this.item.width + this.padding.spacingX);
        let buffHeight = this.bufferSize * 2 * (this.item.height + this.padding.spacingY);
        let minPos = cc.v2(viewPos.x - this.view.anchorX * this.view.width - buffWidth / 2, viewPos.y + (1 - this.view.anchorY) * this.view.height + buffHeight / 2);
        let maxPos = cc.v2(minPos.x + this.view.width + buffWidth, minPos.y - this.view.height - buffHeight);
        let startX = Math.floor((minPos.x + this.content.width * this.content.anchorX - this.padding.left) / (this.item.width + this.padding.spacingX));
        let startY = Math.floor(((this.content.height * (1 - this.content.anchorY)) - minPos.y - this.padding.top) / (this.item.height + this.padding.spacingY));
        let endX = Math.ceil((maxPos.x + this.content.width * this.content.anchorX - this.padding.left) / (this.item.width + this.padding.spacingX));
        let endY = Math.ceil(((this.content.height * (1 - this.content.anchorY)) - maxPos.y - this.padding.top) / (this.item.height + this.padding.spacingY));
        startX = this.clmaNum(0,this.colCount,startX);
        startY = this.clmaNum(0,this.lineCount,startY);
        endX = this.clmaNum(0,this.colCount,endX);
        endY = this.clmaNum(0,this.lineCount,endY);
        console.log(startX, startY, endX, endY);
        for (let i = startY; i < endY; i++) {
            for (let j = startX; j < endX; j++) {
                let info = this.pointMatrix[i][j];
                let bool = this.checkItem(viewRect, info);
                if (bool) {
                    this.contentArr.push(info);
                }
            }
        }
    }
    //检查离开的item
    private checkItem(viewRect: cc.Rect,info:ListPoint) {
        let pos = info.pos;
        pos = this.content.convertToWorldSpaceAR(pos);
        pos = this.view.convertToNodeSpaceAR(pos);
        let itemRect = cc.rect(pos.x - this.item.width * this.item.anchorX, pos.y - this.item.height * this.item.anchorY, this.item.width, this.item.height);
        if (viewRect.intersects(itemRect)) {
            if (!info.item && info.info !== null) {
                info.item = this.getItem();
                info.item.setParent(this.content);
                info.pos.x = info.pos.x === null ? info.item.x : info.pos.x;
                info.pos.y = info.pos.y === null ? info.item.y : info.pos.y;
                info.item.setPosition(info.pos);
                this.updateItemFun.emit([info.info, info.item]);
            }
            return true;
        } else {
            if (info.item) {
                this.putItem(info.item);
                info.item = null;
            }
            return false;
        }
    }
    //得到一个item；
    private getItem() {
        let item = this.itemPool.get();
        if (!item) {
            item = cc.instantiate(this.item);
        }
        return item;
    }
    //回收一个item
    private putItem(item: cc.Node) {
        this.itemPool.put(item);
    }
    //限制数字的范围
    clmaNum(min:number,max:number,num:number){
        num = num<min?min:num;
        num = num>max?max:num;
        return num;
    }
}
